﻿using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;

namespace Dotway.WPF.Effects
{
    public class AmplitudeModulatedEffect : ShaderEffect
    {
        public enum HalftoningMethod
        {
            AmplitudeModulated18 = 1,
            AmplitudeModulated32 = 2,
            AmplitudeModulated36 = 3,
            AmplitudeModulated128 = 4
        };

        #region Fields

        private static PixelShader pixelShader = new PixelShader();        

        #endregion Fields
        #region Constructors

        static AmplitudeModulatedEffect()
        {
            pixelShader.UriSource = Global.MakePackUri("AmplitudeModulatedEffect.ps");
        }

        public AmplitudeModulatedEffect()
        {
            this.PixelShader = pixelShader;

            AmplitudeMap = CreateAmplitudeMap32();

            UpdateShaderValue(InputProperty);
            UpdateShaderValue(AmplitudeMapProperty);
            UpdateShaderValue(HorizontalBlockCountProperty);
            UpdateShaderValue(VerticalBlockCountProperty);
            UpdateShaderValue(LevelsProperty);
            UpdateShaderValue(AMMatrixSizeProperty);
        }

        #endregion Constructors
        #region Properties
                                          
        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(AmplitudeModulatedEffect), 0);
        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }

        public static readonly DependencyProperty AmplitudeMapProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("AmplitudeMap", typeof(AmplitudeModulatedEffect), 1);
        public Brush AmplitudeMap
        {
            get { return (Brush)GetValue(AmplitudeMapProperty); }
            set { SetValue(AmplitudeMapProperty, value); }
        }

        public static readonly DependencyProperty LevelsProperty = DependencyProperty.Register("Levels", typeof(double), typeof(AmplitudeModulatedEffect), new UIPropertyMetadata(18.0, PixelShaderConstantCallback(2)));
        private double Levels
        {
            get { return (double)GetValue(LevelsProperty); }
            set { SetValue(LevelsProperty, value); }
        }

        public static readonly DependencyProperty AMMatrixSizeProperty = DependencyProperty.Register("AMMatrixSize", typeof(double), typeof(AmplitudeModulatedEffect), new UIPropertyMetadata(6.0, PixelShaderConstantCallback(3)));
        private double AMMatrixSize
        {
            get { return (double)GetValue(AMMatrixSizeProperty); }
            set { SetValue(AMMatrixSizeProperty, value); }
        }

        public static readonly DependencyProperty HorizontalBlockCountProperty = DependencyProperty.Register("HorizontalBlockCount", typeof(double), typeof(AmplitudeModulatedEffect), new UIPropertyMetadata(200.0, PixelShaderConstantCallback(0), CoerceHorizontalBlockCount));
        public double HorizontalBlockCount
        {
            get { return (double)GetValue(HorizontalBlockCountProperty); }
            set { SetValue(HorizontalBlockCountProperty, value); }
        }

        private static object CoerceHorizontalBlockCount(DependencyObject d, object value)
        {
            AmplitudeModulatedEffect effect = (AmplitudeModulatedEffect)d;
            double newSize = (double)value;

            if (newSize < 1.0)
            {
                return effect.HorizontalBlockCount;
            }

            return newSize;
        }


        public static readonly DependencyProperty VerticalBlockCountProperty = DependencyProperty.Register("VerticalBlockCount", typeof(double), typeof(AmplitudeModulatedEffect), new UIPropertyMetadata(200.0, PixelShaderConstantCallback(1), CoerceVerticalBlockCount));
        public double VerticalBlockCount
        {
            get { return (double)GetValue(VerticalBlockCountProperty); }
            set { SetValue(VerticalBlockCountProperty, value); }
        }

        private static object CoerceVerticalBlockCount(DependencyObject d, object value)
        {
            AmplitudeModulatedEffect effect = (AmplitudeModulatedEffect)d;
            double newSize = (double)value;

            if (newSize < 1.0)
            {
                return effect.VerticalBlockCount;
            }

            return newSize;
        }


        public static readonly DependencyProperty MethodProperty = DependencyProperty.Register("Method", typeof(HalftoningMethod), typeof(AmplitudeModulatedEffect), new UIPropertyMetadata(HalftoningMethod.AmplitudeModulated32, new PropertyChangedCallback(OnMethodChanged)));
        public HalftoningMethod Method
        {
            get { return (HalftoningMethod)GetValue(MethodProperty); }
            set { SetValue(MethodProperty, value); }
        }

        #region OnMethodChanged

        private static void OnMethodChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            AmplitudeModulatedEffect control = o as AmplitudeModulatedEffect;
            if (control != null)
            {
                control.OnMethodChanged((HalftoningMethod)e.OldValue, (HalftoningMethod)e.NewValue);
            }
        }

        protected virtual void OnMethodChanged(HalftoningMethod oldValue, HalftoningMethod newValue)
        {
            if (newValue == HalftoningMethod.AmplitudeModulated18)
            {
                AmplitudeMap = CreateAmplitudeMap18();
            }
            else if (newValue == HalftoningMethod.AmplitudeModulated32)
            {
                AmplitudeMap = CreateAmplitudeMap32();
            }
            else if (newValue == HalftoningMethod.AmplitudeModulated36)
            {
                AmplitudeMap = CreateAmplitudeMap36();
            }
            else if (newValue == HalftoningMethod.AmplitudeModulated128)
            {
                AmplitudeMap = CreateAmplitudeMap128();
            }
        }

        #endregion OnMethodChanged 
        #endregion Properties

        private Brush CreateAmplitudeMap18()
        {
            Levels = 19;
            AMMatrixSize = 6;
            PixelFormat pixelFormat = PixelFormats.Gray8;
            int rawStride = (int)(AMMatrixSize * pixelFormat.BitsPerPixel) / 8;
            byte[] rawImage = new byte[] { 9, 6, 5, 10, 13, 14, 7, 1, 4, 12, 18, 15, 8, 2, 3, 11, 17, 16, 10, 13, 14, 9, 6, 5, 12, 18, 15, 7, 1, 4, 11, 17, 16, 8, 2, 3 };

            BitmapSource bitmap = BitmapSource.Create((int)AMMatrixSize, (int)AMMatrixSize, 96, 96, pixelFormat, null, rawImage, rawStride);

            ImageBrush imageBrush = new ImageBrush();
            imageBrush.ImageSource = bitmap;
            return imageBrush;
        }

        private Brush CreateAmplitudeMap32()
        {
            Levels = 33;
            AMMatrixSize = 8;
            PixelFormat pixelFormat = PixelFormats.Gray8;
            int rawStride = (int)(AMMatrixSize * pixelFormat.BitsPerPixel) / 8;
            byte[] rawImage = new byte[] { 14, 5, 6, 9, 19, 28, 27, 24, 
									 12, 4, 1, 7, 21, 29, 32, 26, 
									 13, 3, 2, 8, 20, 30, 31, 25, 
									 16, 10, 11, 15, 17, 23, 22, 18, 
									 19, 28, 27, 24, 14, 5, 6, 9, 
									 21, 29, 32, 26, 12, 4, 1, 7, 
									 20, 30, 31, 25, 13, 3, 2, 8, 
									 17, 23, 22, 18, 16, 10, 11, 15};

            BitmapSource bitmap = BitmapSource.Create((int)AMMatrixSize, (int)AMMatrixSize, 96, 96, pixelFormat, null, rawImage, rawStride);

            ImageBrush imageBrush = new ImageBrush();
            imageBrush.ImageSource = bitmap;
            return imageBrush;
        }

        private Brush CreateAmplitudeMap36()
        {
            Levels = 37;
            AMMatrixSize = 6;
            PixelFormat pixelFormat = PixelFormats.Gray8;
            int rawStride = (int)(AMMatrixSize * pixelFormat.BitsPerPixel) / 8;
            byte[] rawImage = new byte[] { 35, 29, 14, 13, 28, 34, 
									     30, 15, 9, 4, 8, 27, 
									     18, 10, 5, 1, 3, 12,
									     22, 17, 6, 2, 7, 23, 
									     31, 21, 16, 11, 24, 26, 
									     36, 32, 20, 19, 25, 33};

            BitmapSource bitmap = BitmapSource.Create((int)AMMatrixSize, (int)AMMatrixSize, 96, 96, pixelFormat, null, rawImage, rawStride);

            ImageBrush imageBrush = new ImageBrush();
            imageBrush.ImageSource = bitmap;
            return imageBrush;
        }

        private Brush CreateAmplitudeMap128()
        {
            Levels = 129;
            AMMatrixSize = 16;
            PixelFormat pixelFormat = PixelFormats.Gray8;
            int rawStride = (int)(AMMatrixSize * pixelFormat.BitsPerPixel) / 8;
            byte[] rawImage = new byte[]{ 64, 58, 50, 40, 39, 49, 57, 63, 65, 71, 79, 89, 90, 80, 72, 66, 
									      59, 34, 27, 18, 17, 26, 33, 56, 70, 95, 102, 111, 112, 103, 96, 73, 
									      51, 28, 14, 5, 6, 9, 25, 48, 78, 101, 115, 124, 123, 120, 104, 81, 
									      41, 19, 12, 4, 1, 7, 24, 38, 88, 110, 117, 125, 128, 122, 105, 91, 
									      42, 20, 13, 3, 2, 8, 23, 37, 87, 109, 116, 126, 127, 121, 106, 92, 
									      52, 29, 16, 10, 11, 15, 32, 47, 77, 100, 113, 119, 118, 114, 97, 82, 
									      60, 35, 30, 21, 22, 31, 36, 55, 69, 94, 99, 108, 107, 98, 93, 74, 
									      61, 53, 45, 43, 44, 46, 54, 62, 68, 76, 84, 86, 85, 83, 75, 67, 
									      65, 71, 79, 89, 90, 80, 72, 66, 64, 58, 50, 40, 39, 49, 57, 63, 
									      70, 95, 102, 111, 112, 103, 96, 73, 59, 34, 27, 18, 17, 26, 33, 56, 
									      78, 101, 115, 124, 123, 120, 104, 81, 51, 28, 14, 5, 6, 9, 25, 48, 
									      88, 110, 117, 125, 128, 122, 105, 91, 41, 19, 12, 4, 1, 7, 24, 38, 
									      87, 109, 116, 126, 127, 121, 106, 92, 42, 20, 13, 3, 2, 8, 23, 37, 
									      77, 100, 113, 119, 118, 114, 97, 82, 52, 29, 16, 10, 11, 15, 32, 47, 
									      69, 94, 99, 108, 107, 98, 93, 74, 60, 35, 30, 21, 22, 31, 36, 55, 
									      68, 76, 84, 86, 85, 83, 75, 67, 61, 53, 45, 43, 44, 46, 54, 62};

            BitmapSource bitmap = BitmapSource.Create((int)AMMatrixSize, (int)AMMatrixSize, 96, 96, pixelFormat, null, rawImage, rawStride);

            ImageBrush imageBrush = new ImageBrush();
            imageBrush.ImageSource = bitmap;
            return imageBrush;
        }


    }
}
